from django.shortcuts import render
from django.views import View
from QQLoginTool.QQtool import OAuthQQ
from django.conf import settings
from django import http
from django.contrib.auth import login
from apps.oauth.models import OAuthQQUser
from apps.oauth.utils import generate_access_token, check_access_token
import json, re
from django_redis import get_redis_connection
from apps.users.models import User
from django.db import DatabaseError


# Create your views here.

class QQURLView(View):
    """给前端返回扫码页面的地址"""

    def get(self, request):
        # 从前端获取next 如果没有传 默认值为 /
        next = request.GET.get('next', '/')
        # 创建OAuthQQ类对象
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                        client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI,
                        state=next)
        # 调用对象的获取 qq 地址方法
        login_url = oauth.get_qq_url()
        # 给前端返回扫码页面地址
        return http.JsonResponse({'code': 0, 'errmsg': 'ok', 'login_url': login_url})


class QQUserView(View):
    def get(self, request):
        # QQ服务器响应一个地址给美多商城，地址中携带code，提取code
        code = request.GET.get('code')
        if not code:
            # 判断 code 参数是否存在
            return http.JsonResponse({'code': 400, 'errmsg': '缺少code参数'})

        # 创建OAuthQQ类对象
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
                        client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI, )
        try:
            # 调用对象,通过code 获取access_token
            access_token = oauth.get_access_token(code)
            # 调用对象,通过 access_token 获取 openid
            openid = oauth.get_open_id(access_token)
        except Exception as e:
            return http.JsonResponse({'code': 400, 'errmsg': 'OAuth2.0认证失败'})

        # ==================================================
        # 判断 openid 是否绑定过美多商城用户
        try:
            # 查看是否有 openid 对应的用户
            # 为啥要用get 因为get查不到就报异常，可以捕获异常，这个异常可以用任何绑定的模型类都可以调用
            oauth_qq = OAuthQQUser.objects.get(openid=openid)

        except OAuthQQUser.DoesNotExist:
            # 如果 openid 没绑定美多商城用户,进入这里:

            # 为了能够在后续的绑定用户操作中前端可以使用 openid，在这里将 openid 签名后响应给前端。
            # openid 属于用户的隐私信息，所以需要将 openid 签名处理，避免暴露。
            # 调用我们自定义的方法, 对 openid 进行加密
            # 把 openid 变为 access_token
            access_token = generate_access_token(openid)

            # 把 access_token 返回给前端，为什么要返回给前端，因为美多商城存openid太麻烦，还要自己存储，识别用户是谁，直接发给前端，前端再携带回来给后端
            # 注意: 这里一定不能返回 0 的状态码. 否则不能进行绑定页面，和前端商议结果，不是错误400也不是工作完成0，所以状态码设置为300
            return http.JsonResponse({'code': 300, 'errmsg': 'ok', 'access_token': access_token})
        else:
            # 如果 openid 已绑定美多商城用户
            # 根据 user 外键, 获取对应的 QQ 用户(user)
            # 实现QQ登录不是用QQ去登录美多商城，而是用QQ绑定的美多商城用户user去登录美多商城
            user = oauth_qq.user

            # 实现状态保持
            login(request, user)

            # 创建重定向到主页的对象
            response = http.JsonResponse({'code': 0, 'errmsg': 'ok'})
            # 将用户信息写入到 cookie 中，有效期14天
            response.set_cookie('username', user.username, max_age=3600 * 24 * 14)
            # 返回响应
            return response

    def post(self, request):
        """美多商城用户绑定到openid"""

        # 1.接收参数
        dict = json.loads(request.body.decode())
        mobile = dict.get('mobile')
        password = dict.get('password')
        sms_code_client = dict.get('sms_code')
        access_token = dict.get('access_token')

        # 2.校验参数
        # 判断参数是否齐全
        if not all([mobile, password, sms_code_client,access_token]):
            return http.JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})

        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({'code': 400, 'errmsg': '请输入正确的手机号码'})

        # 判断密码是否合格
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.JsonResponse({'code': 400, 'errmsg': '请输入8-20位的密码'})

        # 3.判断短信验证码是否一致
        # 创建 redis 链接对象:
        redis_conn = get_redis_connection('verify_code')

        # 从 redis 中获取 sms_code 值:
        sms_code_server = redis_conn.get('sms_%s' % mobile)

        # 判断获取出来的有没有:
        if sms_code_server is None:
            # 如果没有, 直接返回:
            return http.JsonResponse({'code': 400, 'errmsg': '验证码失效'})
        # 如果有, 则进行判断:
        if sms_code_client != sms_code_server.decode():
            # 如果不匹配, 则直接返回:
            return http.JsonResponse({'code': 400, 'errmsg': '输入的验证码有误'})

        # 调用我们自定义的函数, 检验传入的 access_token 是否正确:
        # 错误提示放在 sms_code_errmsg 位置
        openid = check_access_token(access_token)
        if not openid:
            return http.JsonResponse({'code': 400, 'errmsg': '缺少openid'})
        # 4.保存注册数据
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 用户不存在,新建用户
            user = User.objects.create_user(username=mobile, password=password, mobile=mobile)
        else:
            # 如果用户存在，检查用户密码
            if not user.check_password(password):
                return http.JsonResponse({'code': 400, 'errmsg': '输入的密码不正确'})
        # 5.将用户绑定 openid
        # create_user()只有继承自AbstractUser的用户模型类才能调用
        # create()凡是继承自Model的模型类都可以调用，用来创建记录
        try:
            OAuthQQUser.objects.create(openid=openid, user=user)
        except DatabaseError:
            return http.JsonResponse({'code': 400, 'errmsg': '往数据库添加数据出错'})

        # 6.实现状态保持
        login(request, user)

        # 7.创建响应对象:
        response = http.JsonResponse({'code': 0, 'errmsg': 'ok'})

        # 8.登录时用户名写入到 cookie，有效期14天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 14)

        # 9.响应
        return response
